﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Look_Through_Windows.Helper;
using Look_Through_Windows.Network;

namespace Look_Through_Windows.Logic
{
    /// <summary>
    /// A remote shared window (from remote)
    /// </summary>
    class RemoteWindow
    {
        /// <summary>
        /// Use debug rects?
        /// </summary>
        public bool DebugRects = false;

        /// <summary>
        /// Shared HWND
        /// </summary>
        public IntPtr Hwnd;
        /// <summary>
        /// Local window
        /// </summary>
        public Window LocalWindow;
        /// <summary>
        /// Name of that window
        /// </summary>
        public string Name;

        /// <summary>
        /// The sharer
        /// </summary>
        public NetConnection Sharer;

        /// <summary>
        /// Last bitmap
        /// </summary>
        public WriteableBitmap LastBitmap;

        /// <summary>
        /// Set of update rects
        /// </summary>
        private readonly HashSet<Rectangle> updateRects = new HashSet<Rectangle>( );
        /// <summary>
        /// Rect panel
        /// </summary>
        private Canvas rectPanel;

        /// <summary>
        /// still shraing?
        /// </summary>
        private bool sharing = false;

        /// <summary>
        /// Updates this window from a buffer
        /// </summary>
        public void UpdateBitmap( NetMessage msg )
        {
            bool clear = msg.ReadBool( "clear" );
            int left = msg.ReadInt( "left" );
            int top = msg.ReadInt( "top" );
            int right = msg.ReadInt( "right" );
            int bottom = msg.ReadInt( "bottom" );
            RECT clientRect = new RECT( ) { Left = left, Top = top, Right = right, Bottom = bottom };

            List<RECT> newRects = new List<RECT>( );

            if ( clear )
            {
                byte[] buffer = msg.ReadByteArray( "content" );
                LastBitmap = buffer.ToPNG( );
            }
            else
            {
                int[] curr_pixels = new int[ LastBitmap.PixelWidth * LastBitmap.PixelHeight ];
                LastBitmap.CopyPixels( curr_pixels, 4 * LastBitmap.PixelWidth, 0 );

                int rectCount = msg.ReadInt( "count" );
                for ( int _c = 0 ; _c < rectCount ; ++_c )
                {
                    RECT updateRect = new RECT( );
                    updateRect.Left = msg.ReadInt( "left-u" );
                    updateRect.Top = msg.ReadInt( "top-u" );
                    updateRect.Right = msg.ReadInt( "right-u" );
                    updateRect.Bottom = msg.ReadInt( "bottom-u" );
                    byte[] buffer = msg.ReadByteArray( "content" );
                    newRects.Add( updateRect );

                    int uwidth = updateRect.Right - updateRect.Left;
                    int uheight = updateRect.Bottom - updateRect.Top;
                    int[] new_pixels = new int[ uwidth * uheight ];
                    WriteableBitmap bmp = buffer.ToPNG( );
                    bmp.CopyPixels( new_pixels, 4 * uwidth, 0 );
                    for ( int y = updateRect.Top ; y < updateRect.Bottom ; ++y )
                        for ( int x = updateRect.Left ; x < updateRect.Right ; ++x )
                            curr_pixels[ y * LastBitmap.PixelWidth + x ] ^= new_pixels[ ( y - updateRect.Top ) * uwidth + ( x - updateRect.Left ) ];
                }

                LastBitmap.WritePixels( new Int32Rect( 0, 0, LastBitmap.PixelWidth, LastBitmap.PixelHeight ), curr_pixels, 4 * LastBitmap.PixelWidth, 0 );
            }

            if ( !sharing ) return;

            if ( LocalWindow == null )
            {
                LocalWindow = new Window( );
                LocalWindow.Width = 200;
                LocalWindow.Height = 200;
                LocalWindow.Closing += LocalWindow_Closing;
                LocalWindow.Show( );
            }

            IntPtr hwnd = ( ( HwndSource )HwndSource.FromVisual( LocalWindow ) ).Handle;
            RECT lr;
            WindowExtract.GetClientRect( hwnd, out lr );

            //LocalWindow.PointToScreen( new Point( 0, 0 ) );

            double marginLeft = lr.Left;
            double marginTop = lr.Top;
            double marginRight = LocalWindow.Width - lr.Right;
            double marginBottom = LocalWindow.Height - lr.Bottom;

            int width = clientRect.Right - clientRect.Left;
            int height = clientRect.Bottom - clientRect.Top;

            //Console.WriteLine( width + " x " + height );

            int[] colors = new int[ width * height ];
            LastBitmap.CopyPixels( new Int32Rect( clientRect.Left, clientRect.Top, width, height ), colors, width * 4, 0 );
            WriteableBitmap cropbmp = new WriteableBitmap( width, height, 96, 96, PixelFormats.Bgr32, null );
            cropbmp.WritePixels( new Int32Rect( 0, 0, width, height ), colors, width * 4, 0 );

            LocalWindow.Width = width + marginLeft + marginRight;
            LocalWindow.Height = height + marginTop + marginBottom;
            LocalWindow.Background = new ImageBrush( cropbmp );

            LocalWindow.Title = Name + " (@ " + Sharer.Tag + ")";

            // update rects
            if ( DebugRects )
            {
                if ( rectPanel == null )
                {
                    rectPanel = new Canvas( );
                    LocalWindow.Content = rectPanel;
                }
                foreach ( var rect in updateRects )
                {
                    rectPanel.Children.Remove( rect );
                }
                foreach ( var newRect in newRects )
                {
                    rectPanel.Width = right - left;
                    rectPanel.Height = bottom - top;
                    //rectPanel.HorizontalAlignment = HorizontalAlignment.Left;
                    //rectPanel.VerticalAlignment = VerticalAlignment.Top;

                    Rectangle rect = new Rectangle( );
                    rect.Width = newRect.Right - newRect.Left + 2;
                    rect.Height = newRect.Bottom - newRect.Top + 2;
                    rect.Fill = Brushes.Transparent;
                    rect.Stroke = Brushes.Magenta;
                    //rect.HorizontalAlignment = HorizontalAlignment.Left;
                    //rect.VerticalAlignment = VerticalAlignment.Top;
                    rect.Arrange( new Rect( newRect.Left - 1, newRect.Top - 1, newRect.Right - newRect.Left + 2, newRect.Bottom - newRect.Top + 2 ) );
                    Canvas.SetLeft( rect, newRect.Left - 1 - clientRect.Left );
                    Canvas.SetTop( rect, newRect.Top - 1 - clientRect.Top );
                    rectPanel.Children.Add( rect );
                    updateRects.Add( rect );
                }
            }
        }

        void LocalWindow_Closing( object sender, System.ComponentModel.CancelEventArgs e )
        {
            LocalWindow = null;
            CancelShared( );
        }

        /// <summary>
        /// Request sharing
        /// </summary>
        public void RequestSharing( )
        {
            NetMessage msg = new NetMessage( SendCodes.ShareWindowReq );
            msg.Write( Hwnd.ToInt64( ), "hwnd" );
            Sharer.Send( msg );

            sharing = true;
        }

        public override string ToString( )
        {
            return Name + " (" + Hwnd.ToInt32( ).ToString( "X" ) + " from " + ( Sharer.Tag ?? Sharer ) + ")";
        }

        /// <summary>
        /// Cancels sharing if shared
        /// </summary>
        public void CancelShared( )
        {
            if ( !sharing ) return;

            NetMessage msg = new NetMessage( SendCodes.ShareWindowStop );
            msg.Write( Hwnd.ToInt64( ), "hwnd" );
            Sharer.Send( msg );

            sharing = false;

            if ( LocalWindow != null )
            {
                LocalWindow.Close( );
                LocalWindow = null;
            }
        }
    }
}
